package com.xuecheng.learning.service.impl;

import cn.hutool.core.bean.BeanUtil;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.xuecheng.api.client.ContentClient;
import com.xuecheng.api.dto.CoursePublish;
import com.xuecheng.base.constant.CourseChargeConstant;
import com.xuecheng.base.constant.LearningChooseCourseStatusConstant;
import com.xuecheng.base.constant.LearningOrderTypeConstant;
import com.xuecheng.base.exception.CommonError;
import com.xuecheng.base.exception.XCPlusBaseException;
import com.xuecheng.base.util.SecurityUtil;
import com.xuecheng.learning.exception.LearningCourseHasBeenChosenException;
import com.xuecheng.learning.exception.LearningHasSameChooseCourseNotPayException;
import com.xuecheng.learning.mapper.XcChooseCourseMapper;
import com.xuecheng.learning.model.dto.XcChooseCourseDTO;
import com.xuecheng.learning.model.dto.XcCourseTablesDTO;
import com.xuecheng.learning.model.po.XcChooseCourse;
import com.xuecheng.learning.model.po.XcCourseTables;
import com.xuecheng.learning.service.IXcChooseCourseService;
import com.xuecheng.learning.service.IXcCourseTablesService;
import lombok.RequiredArgsConstructor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;

import java.time.LocalDateTime;
import java.util.Objects;

/**
 * <p>
 * 服务实现类
 * </p>
 *
 * @author 勾新杰
 * @since 2025-02-26
 */
@Service
@RequiredArgsConstructor
public class XcChooseCourseServiceImpl extends ServiceImpl<XcChooseCourseMapper, XcChooseCourse> implements IXcChooseCourseService {

    /**
     * 内容管理客户端
     */
    private final ContentClient contentClient;

    /**
     * 课程表mapper
     */
    private final IXcCourseTablesService courseTablesService;

    /**
     * 课程表mapper
     */
    private final XcChooseCourseMapper xcChooseCourseMapper;

    /**
     * 添加选课
     *
     * @param courseId 课程id
     * @return 选课记录和状态
     */
    @Override
    @Transactional
    public XcChooseCourseDTO addChooseCourse(Long courseId) {
        // 0. 获取用户id
        SecurityUtil.XcUser user = SecurityUtil.getUser();
        String userId = Objects.requireNonNull(user).getId();
        // 1. 查询该发布课程相关的课程信息
        CoursePublish coursePublish = contentClient.getById(courseId);
        String charge = coursePublish.getCharge();
        XcChooseCourse chooseCourse;
        // 2. 课程如果免费,走添加免费课程逻辑
        if (CourseChargeConstant.FREE.equals(charge))
            chooseCourse = chooseFreeCourse(coursePublish, userId);
            // 3. 课程如果收费,走添加收费课程逻辑
        else chooseCourse = chooseChargeCourse(coursePublish, userId);
        // 4. 获取学习资格
        XcChooseCourseDTO xcChooseCourseDTO = BeanUtil.copyProperties(chooseCourse, XcChooseCourseDTO.class);
        XcCourseTablesDTO qualification = courseTablesService.getLearningQualification(courseId, userId);
        xcChooseCourseDTO.setLearnStatus(qualification.getLearnStatus());
        // 5. 返回选课记录和状态
        return xcChooseCourseDTO;
    }

    /**
     * 选免费课程
     *
     * @param coursePublish 发布课程信息
     * @param userId        用户id
     * @return 选课信息
     */
    private XcChooseCourse chooseFreeCourse(CoursePublish coursePublish, String userId) {
        // 1. 判断该发布课程此用户是否已经选了的
        Long courseId = coursePublish.getId();
        XcChooseCourse chooseCourse = lambdaQuery()
                .eq(XcChooseCourse::getCourseId, courseId)
                .eq(XcChooseCourse::getUserId, userId)
                .eq(XcChooseCourse::getOrderType, LearningOrderTypeConstant.FREE)
                .eq(XcChooseCourse::getStatus, LearningChooseCourseStatusConstant.CHOOSE_COURSE_SUCCESS)
                .one();
        if (chooseCourse != null
                && chooseCourse.getValidtimeEnd().isAfter(LocalDateTime.now()))
            LearningCourseHasBeenChosenException.cast();
        // 2. 如果没有选或过期,则添加选课
        if(chooseCourse != null) removeById(chooseCourse.getId());
        Long companyId = coursePublish.getCompanyId();
        String courseName = coursePublish.getName();
        Integer validDays = coursePublish.getValidDays();
        chooseCourse = new XcChooseCourse()
                .setCourseId(courseId)
                .setCourseName(courseName)
                .setUserId(userId)
                .setCompanyId(companyId)
                .setOrderType(LearningOrderTypeConstant.FREE)
                .setCreateDate(LocalDateTime.now())
                .setCoursePrice(coursePublish.getPrice())
                .setValidDays(validDays)
                .setStatus(LearningChooseCourseStatusConstant.CHOOSE_COURSE_SUCCESS)
                .setValidtimeStart(LocalDateTime.now())
                .setValidtimeEnd(LocalDateTime.now().plusDays(validDays))
                .setRemarks(coursePublish.getRemark());
        boolean save = save(chooseCourse);
        if (!save) XCPlusBaseException.cast(CommonError.FAILED_INSERT_TO_DATABASE);
        // 3. 加入到课程表中
        courseTablesService.remove(courseTablesService.lambdaQuery()
                .eq(XcCourseTables::getUserId, userId)
                .eq(XcCourseTables::getCourseId, courseId));
        XcCourseTables xcCourseTables = new XcCourseTables()
                .setChooseCourseId(chooseCourse.getId())
                .setUserId(userId)
                .setCourseId(courseId)
                .setCompanyId(companyId)
                .setCourseName(courseName)
                .setCourseType(chooseCourse.getOrderType())
                .setCreateDate(LocalDateTime.now())
                .setValidtimeStart(chooseCourse.getValidtimeStart())
                .setValidtimeEnd(chooseCourse.getValidtimeEnd())
                .setUpdateDate(LocalDateTime.now())
                .setRemarks(coursePublish.getRemark());
        save = courseTablesService.save(xcCourseTables);
        if (!save) XCPlusBaseException.cast(CommonError.FAILED_INSERT_TO_DATABASE);
        // 4. 返回选课信息
        return chooseCourse;
    }

    /**
     * 选收费课程
     *
     * @param coursePublish 发布课程信息
     * @param userId        用户id
     * @return 选课信息
     */
    private XcChooseCourse chooseChargeCourse(CoursePublish coursePublish, String userId) {
        // 1. 判断该发布课程此用户是否已经选了的并且已支付
        Long courseId = coursePublish.getId();
        XcChooseCourse chooseCourse = lambdaQuery()
                .eq(XcChooseCourse::getCourseId, courseId)
                .eq(XcChooseCourse::getUserId, userId)
                .eq(XcChooseCourse::getOrderType, LearningOrderTypeConstant.CHARGE)
                .one();
        if (chooseCourse != null
                && LearningChooseCourseStatusConstant.CHOOSE_COURSE_SUCCESS.equals(chooseCourse.getStatus())
                && chooseCourse.getValidtimeEnd().isAfter(LocalDateTime.now()))
            LearningCourseHasBeenChosenException.cast();
        // 2. 如果该发布课程选了但是未支付
        if (chooseCourse != null
                && LearningChooseCourseStatusConstant.CHOOSE_COURSE_NEED_PAY.equals(chooseCourse.getStatus()))
            return chooseCourse;
        // 3. 选课,加入到选课表,这里不要加入课程表,等到支付过后再添加到课程表
        if(chooseCourse != null) removeById(chooseCourse.getId());
        Integer validDays = coursePublish.getValidDays();
        chooseCourse = new XcChooseCourse()
                .setCourseId(courseId)
                .setCourseName(coursePublish.getName())
                .setUserId(userId)
                .setCompanyId(coursePublish.getCompanyId())
                .setOrderType(LearningOrderTypeConstant.CHARGE)
                .setCreateDate(LocalDateTime.now())
                .setCoursePrice(coursePublish.getPrice())
                .setValidDays(validDays)
                .setStatus(LearningChooseCourseStatusConstant.CHOOSE_COURSE_NEED_PAY)
                .setValidtimeStart(LocalDateTime.now())
                .setValidtimeEnd(LocalDateTime.now().plusDays(validDays))
                .setRemarks(coursePublish.getRemark());
        boolean save = save(chooseCourse);
        if (!save) XCPlusBaseException.cast(CommonError.FAILED_INSERT_TO_DATABASE);
        // 4. 返回选课信息
        return chooseCourse;
    }

    /**
     * 根据id查询选课记录
     *
     * @param id 选课记录id
     * @return 选课
     */
    @Override
    public XcChooseCourse getByIdForUpdate(Long id) {
        return xcChooseCourseMapper.getByIdForUpdate(id);
    }
}
